package main.model.critters;

import java.awt.Color;
import java.awt.Polygon;
import java.util.ArrayList;
import java.util.Random;

import main.demonutils.IslandTime;
import main.model.critters.eventprocessor.CritterEventProcessorImp;
import main.model.critters.genetics.*;
import main.model.hexisland.location.SubHexLocation;
import main.model.island.ColorGenerator;




public class Critter extends CritterEventProcessorImp{
	private static int CURRENTID = 1;
    private static NameFactory nameFactory = new NameFactory();
	
	//Personal Details
	private int id;
	private String name;
	private Sex sex;
	private Critter mother =null , father = null;
	private Chromosome xchromosome; private Chromosome ychromosome;
	private DefaultMorphology morphology;
	private Species species;
    private boolean isLargeCritter;
	
	//Fluxing Stats
	private Color skin;
	private SubHexLocation location;
    private ArrayList<SubHexLocation> path;

	private Polygon shape;
	
	private static final int MAXHEALTH = 1000;
	private int health = MAXHEALTH; //Goes down if hungry or attacked
	private IslandTime age = new IslandTime(0);
	private boolean fertile = false; //Maturity is reached if Age=>Life/6
    private boolean alive = true; //false if dead
    private int death;
	
	private int requiredVeg, vegEaten;
	private int requiredMeat, meatEaten;
	private int requiredFood;

		
	public Critter(Chromosome xchromosome, Chromosome ychromosome, 
			Critter mother, Critter father, Species species, Sex sex)
	{	
		this(xchromosome, ychromosome, species, sex);
		this.mother = mother;	this.father = father;
	}
	
	public Critter(Chromosome xchromosome, Chromosome ychromosome, 
			Species species, Sex sex){
		setSex(sex);
		this.xchromosome = xchromosome;	this.ychromosome = ychromosome;
		this.morphology = new DefaultMorphology(this.sex, ychromosome, ychromosome);
		setId();
        setSpecies(species);
		setName(nameFactory.newName());
		setDeath(100);
		setDiet();
        isLargeCritter = (morphology.getSize()>Gene.getGeneMax()/2) ;
		skin = ColorGenerator.getSkinColor();
		if(mother != null){	
			location = new SubHexLocation(mother.getLocation());
		}else{
			location = new SubHexLocation(0,0,0);
		}
	}

	
	public String getName() {return name;}
	public String getFullName() {return name+" "+species.getName();}
	public void setName(String name) {this.name = name;}
	
	public Species getSpecies() {return species;}
	public void setSpecies(Species species) {this.species = species;}
	
	public int getId() {return id;}	
	public Sex getSex() {return sex;}
	public Critter getMother() {return mother;}
	public Critter getFather() {return father;}
	public Chromosome getXchromosome() {return xchromosome;}
	public Chromosome getYchromosome() {return ychromosome;}
	public DefaultMorphology getMorphology() {return morphology;}
	
	public boolean isMature(){return fertile;}
	public boolean isAlive(){return alive;}
	public void kill(){
		alive=false;
		fireDeathEvent(this);
	}
	
	public IslandTime getAge() {return age;}
	public void progressAge() {age.addMinutes(1);}
	public void ageCheck(){
		if(alive){
			if(!fertile && age.getWeeks() > morphology.getLifespan()){
				fertile = true;
			}
			if(age.getWeeks() > death){
				kill();
			}
		}
	}
	
	public void eatVeg(int food){
		vegEaten = vegEaten + food;
	}
	public void eatMeat(int food){
		meatEaten = meatEaten + food;
	}
	
	public int hungerMeat(){
		if(meatEaten<requiredMeat){
			return requiredMeat-meatEaten;
		}else{
			return requiredFood - (meatEaten + vegEaten);
		}
	}
	public int hungerVeg(){
		if(vegEaten<requiredVeg){
			return requiredVeg-vegEaten;
		}else{
			return requiredFood - (meatEaten + vegEaten);
		}
	}
	
	public Color getSkin() {return skin;}
	public void setSkin(Color skin) {this.skin = skin;}


	public SubHexLocation getLocation(){return location;}
    public void setLocation(SubHexLocation location){this.location = location;}

    public ArrayList<SubHexLocation> getLocationPath(){return path;}
    public void setLocationPath(ArrayList<SubHexLocation> path){this.path = path;}


	public Polygon getShape() {return shape;}
	public void setShape(Polygon shape) {this.shape = shape;}

	//Housekeeping methods

	private void setSex(Sex sex){
		if(sex == null || sex == Sex.NONE){
			this.sex = CritterFactory.randomSex();
		}else{
			this.sex = sex;
		}
	}
	
	private void setId() {
		this.id = CURRENTID;
		CURRENTID = CURRENTID+1;
	}
	
	/**
	 * a death age between life*5 and life*7 with a standard distribution
	 * @param degree
	 */
 	private void setDeath(int degree){
		int life = morphology.getLifespan()+1;
		death = life*5;
		Random random = new Random();
		int count = 0;
		for(int i = 0; i<degree;i++){
			count += random.nextInt(life*2);
		}
		death += (count/degree);
	}

	private void setDiet(){
		int diet = morphology.getDiet();
		int dietMax = Gene.getGeneMax();
        requiredFood = dietMax;

		if(diet<dietMax/5){
			requiredVeg = requiredFood;
			requiredMeat=0;
		}else if(diet<(dietMax/5)*2){
			requiredVeg = requiredFood - ((requiredFood/(dietMax/5)) * (diet-(dietMax/5)*2));
			requiredMeat = 0;
		}else if(diet<(dietMax/5)*3){
			requiredVeg = 0;
			requiredMeat = 0;
		}else if(diet<(dietMax/5)*4){
			requiredMeat = ((requiredFood/(dietMax/5)) * (diet-(dietMax/5)*4));
			requiredVeg = 0;
		}else{
			requiredMeat = requiredFood;
			requiredVeg = 0;
		}
	}

	public String getIdentity() {return "#"+getId()+" ("+getFullName()+")";}

    @Override
    public String toString(){
        return getIdentity();
    }

    @Override
    public int hashCode(){
        int hash = 7;
        int prime = 31;
        hash = prime * hash + id;
        //hash = seed * hash + (null == data ? 0 : data.hashCode());
        return hash;
    }

    /**
     * If the object is a critter, the ids are compared.
     * @param obj
     * @return
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (!(obj instanceof Critter))
            return false;
        if (obj == this)
            return true;
        Critter test = (Critter)obj;
        if(test.getId() == this.getId())
            return true;
        else
            return false;
    }

    public boolean isLargeCritter() {
        return isLargeCritter;
    }
}